package com.gitee.huanminabc.jcommon.file;

import com.gitee.huanminabc.jcommon.exception.CommonException;
import com.gitee.huanminabc.jcommon.str.StringUtil;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 *   打包为zip压缩文件,和解压缩
 */
public class ZipFileUtil {
    /**
     * 压缩文件方法
     *
     * @param fileList 需要压缩的文件列表
     * @param destFile 目标文件
     * @throws IOException 异常信息
     */
    public static void zip(String excludePrefix,List<File> fileList, File destFile) throws IOException {
        if (fileList == null || fileList.isEmpty()) {
            return;
        }
        //zip输出流对象
        try(ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(destFile.toPath())))) {
            //设置压缩等级，level (0-9)
            zipOutputStream.setLevel(5);
            //遍历文件列表
            zip( excludePrefix,zipOutputStream,fileList);
            zipOutputStream.flush();
            zipOutputStream.finish();//完成压缩操作必须在outputStream.toByteArray()之前调用否则压缩文件会损坏
        }

    }


    /**
     *
     * @param excludePrefix   假设D:\\test\\code-generator\\huanmin\\com\\util\\test\\config\\SwaggerConfig.java  ,
     *                        比如excludePrefix为code-generator\\huanmin ,最后会生成com\\util\\test\\config\\SwaggerConfig.java ,路径符号会自动转换为当前系统的路径符号
     *                        如果excludePrefix为null或者空字符串,则会生成config\\SwaggerConfig.java
     * @param zipOutputStream zip输出流对象
     * @param fileList 需要压缩的文件列表
     * @throws IOException
     */
    private static void zip(String excludePrefix, ZipOutputStream zipOutputStream,List<File> fileList) throws IOException {
        zipOutputStream.setLevel(5);
        //遍历文件列表
        for (File file : fileList) {
            if (file.isDirectory()) {
                zip( excludePrefix,zipOutputStream, Arrays.asList(Objects.requireNonNull(file.listFiles())));
            }
            if (file.isFile()){
                String nameFile="";
                if (StringUtil.isNotEmpty(excludePrefix)) {
                    excludePrefix=Paths.get(excludePrefix).toString();
                    //如果有排除的前缀,则去掉排除的前缀
                    //获取前缀下标
                    int index = file.getAbsolutePath().indexOf(excludePrefix);
                    if (index==-1){
                        throw new CommonException("排除前缀不存在 excludePrefix:{},file:{}",excludePrefix,file.getAbsolutePath());
                    }
                    //获取排除前缀后的路径
                    nameFile = file.getAbsolutePath().substring(index+excludePrefix.length()+1);
                }else{
                    //zip文件条目对象
                    String name = file.getParentFile().getName();
                    nameFile = !name.isEmpty() ?nameFile+File.separator +name+File.separator + file.getName():file.getName();
                }
                //将文件条目对象放入zip流中
                zipOutputStream.putNextEntry(new ZipEntry(nameFile));
                //将文件信息写入zip流中
                FileInputStream fileInputStream = new FileInputStream(file);
                byte[] bytes = new byte[1024];
                int len;
                while ((len = fileInputStream.read(bytes)) != -1) {
                    zipOutputStream.write(bytes, 0, len);
                }
                zipOutputStream.closeEntry();
                fileInputStream.close();
            }

        }
    }
    public static  byte[] zip(String excludePrefix, List<File> fileList) throws IOException {
        try ( ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
              ZipOutputStream zip = new ZipOutputStream(outputStream);
              ){
            zip(excludePrefix,zip,fileList);
            zip.flush();
            zip.finish();//完成压缩操作必须在outputStream.toByteArray()之前调用否则压缩文件会损坏
            return outputStream.toByteArray();
        }
    }
    //压缩目录
    public static  byte[] zipDir( String excludePrefix,File dir) throws IOException {
        return zip(excludePrefix,Arrays.asList(Objects.requireNonNull(dir.listFiles())));
    }

    /**
     * 解压缩zip文件
     *
     * @param zipFile  待解压的文件
     * @param destFile 解压到哪个目录
     */
    public static void unzip(File zipFile, File destFile) {
        //目标文件必须是一个目录
        if (destFile.isFile()) {
            return;
        }
        //文件夹不存在要先创建文件夹
        if (!destFile.exists()) {
            destFile.mkdirs();
        }
        //创建一个zip读取流，在try中直接写会自动关闭流对象，无需手动关闭
        try (ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)))) {
            ZipEntry zipEntry;
            //遍历读取文件条目，读取下一个 ZIP文件条目并将流定位在条目数据的开头。
            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                String name = zipEntry.getName();
                //构建一个新文件对象，创建在destFile目录下
                File f = new File(destFile, name);
                //判断目录是否存在如果不存在那么就创建
                File parentFile = f.getParentFile();
                if (!parentFile.exists()) {
                    parentFile.mkdirs();
                }
                //将文件条目数据写入到新的文件对象中，
                try (FileOutputStream fileOutputStream = new FileOutputStream(f, false)){
                    byte[] bytes = new byte[1024];
                    int len;
                    while ((len = zipInputStream.read(bytes)) != -1) {
                        fileOutputStream.write(bytes, 0, len);
                    }
                    zipInputStream.closeEntry();
                }
            }
        } catch (IOException e) {
             throw new CommonException(e);
        }
    }


}
